home *** CD-ROM | disk | FTP | other *** search
/ Mac-Source 1994 July / Mac-Source_July_1994.iso / C and C++ / Utilities / Winter Shell 1.0d2 / Source / Libraries / PatchLib / PatchLib.c next >
Encoding:
C/C++ Source or Header  |  1994-01-14  |  9.0 KB  |  291 lines  |  [TEXT/KAHL]

  1. /* Use this file to write patches to traps. You can create as many
  2.     patches as you like. Each patch is created as a non-relocatable block in
  3.     the heap. All patches are automatically removed when ExitToShell is
  4.     called. Removing all patches before the application exits doesn't seem
  5.     to be necessary with MultiFinder, but under Finder you must remove
  6.     the patches before exiting.
  7.     
  8.     You don't have to setup register A5 (or A4 for code resources)
  9.     since the patch glue will already have taken care of it. There's
  10.     also no need to save any registers. Finally, the patch glue ensures
  11.     that you can't write a tail patch; if you really want to do so,
  12.     you'll have to modify this library.
  13.  
  14.     (Comment written 93/11/24)
  15.     
  16.     The patch routine must be declared:
  17.         
  18.         pascal <return_type> <function_name> ( PatchType *patch[,...])
  19.     
  20.     For instance, to patch ReadDateTime, declare a function:
  21.     
  22.         pascal OSErr PatchReadDateTime(PatchType *patch, long *secs)
  23.         {
  24.             ... do patch ...
  25.             return(noErr);
  26.         }
  27.     
  28.     then call PatchBegin to install the patch:
  29.     
  30.         PatchType *p;
  31.         p = PatchBegin(PatchReadDateTime, _ReadDateTime, sizeof(long *),
  32.                             sizeof(OSErr), data)
  33.     
  34.     The parameters to PatchBegin are:
  35.     
  36.         addr        address of patch routine;
  37.         num        number of trap to be patched;
  38.         argsize    size, in bytes, of arguments to patch (not including the first
  39.                     PatchType * patch;
  40.         retsize    size, in bytes, of value returned from patch;
  41.         data        application defined data, may be accessed from patch->data
  42.                     in the patch routine;
  43.                     
  44.     For argsize and retsize, remember that the stack must always be
  45.     word-alligned. This means that bytes are pushed on the stack
  46.     as words, so a char value really occupies 2 bytes. You can use
  47.     the typedefs ParameterBooleanParameter, ParameterByteParameter,
  48.     and ParameterCharParameter to get the size of byte sized
  49.     parameters and return values.
  50.     
  51.     You can temporarily remove the patch with PatchRemove and reinstall it with
  52.     PatchInstall. To permanently remove the patch and dispose of the memory it
  53.     occupies call PatchEnd.
  54.     
  55.     In some situations you may want to prevent the original trap routine from
  56.     executing. To do so, set the flag patch->skip to true in your patch routine.
  57.     The flag is reset after your patch routine returns. Normally, the return
  58.     value from a patch routine is ignored, since the original trap routine
  59.     returns a value which overwrites the value returned by the patch routine.
  60.     However, if patch->skip is true, then the value returned by the patch
  61.     routine is returned as the result of the trap. */
  62.  
  63. /*    Revision History:
  64.  
  65.     94/01/14 aih
  66.     - added typedefs for Boolean, char, and Byte parameters
  67.     
  68.     93/12/22 aih
  69.     - uses NewPtrClear/DisposPtr instead of calling MemoryLib functions,
  70.     so that we don't need to depend on or intialize MemoryLib
  71.     
  72.     93/11/22 aih
  73.     - totally rewrote so it no longer uses a self-modifying code resource
  74.     to save the original address of the trap, instead the code and data
  75.     about a patch are stored in a non-relocatable block
  76.     
  77.     93/11/20 aih
  78.     - modified so patch can prevent execution of original trap
  79.     
  80.     93/03/26 AIH
  81.     - Changed PatchType to void* instead of pointer to function returning
  82.     void
  83.         
  84.     91/05/15 AIH
  85.     - Added a few comments
  86.     
  87.     91/04/19 AIH
  88.     - The trap type is determined at run-time, so there's no need to pass
  89.     the trap type as a parameter to the patch install function
  90.     
  91.     91/03/23 AIH
  92.     - StripAddress is called before calling NSetTrapAddress. This probably
  93.     is not needed, but TN#213 recommends calling StripAddress before patching
  94.     traps (ok, only for traps that start at a block, if I read it correctly),
  95.     but the issue is so confusing it seems simplest just to call StripAddress.
  96.     
  97.     91/02/28 AIH
  98.     - Added default definitions for parameter declarations
  99.     
  100.     91/01/18 Ari Halberstadt (AIH)
  101.     - Wrote this generic patch handling function/macro combination. */
  102.  
  103. #include "PatchLib.h"
  104.  
  105. /* list of patches */
  106. static PatchType *gPatches;
  107.  
  108. static void _patch_code_bounds(void);
  109.  
  110. static pascal void _patch_code(void)
  111. {
  112.     #define patch(x)    PatchType.x(patchreg)
  113.     #define patchreg    a2                // register containing pointer to patch data
  114.     #define globreg    a5                // register containing base of global variables
  115.     #define argszreg    d3                // register containing size of arguments
  116.     #define retszreg    d4                // register containing size of return value
  117.     #define rsvoffset    4                // offset from a6 to reserved memory
  118.     #define argoffset    12                // offset from a6 to last arg
  119.     #define saveregs    a0-a4/d0-d7    // registers saved/restored with movem
  120.             
  121.     asm {
  122.  
  123.     start:
  124.             clr.l        -(sp)                                ; reserve space for trap address
  125.             link        a6, #0                            ; create stack frame
  126.             movem.l    saveregs, -(sp)                ; save registers
  127.              jsr        @data                                ; get patch data
  128.             jsr        @call                                ; execute patch
  129.             tst.b        patch(skip)                        ; check if should skip routine
  130.             bne.s        @skip
  131.              movem.l    (sp)+, saveregs                ; restore registers
  132.              unlk        a6                                    ; pop stack frame
  133.             rts                                            ; execute original routine
  134.     
  135.             ; skip patch
  136.     skip:
  137.             clr.b        patch(skip)                        ; clear flag
  138.             move.l    argszreg, rsvoffset(a6)        ; save size of args
  139.              movem.l    (sp)+, saveregs                ; restore registers
  140.              unlk        a6                                    ; pop stack frame
  141.             move.l    (sp)+, d0                        ; pop args size
  142.             move.l    (sp)+, a0                        ; pop return address
  143.             add.l        d0, sp                            ; pop args
  144.             jmp        (a0)                                ; return to caller
  145.     
  146.              ; get pointer to patch data
  147.     data:
  148.              lea        @start, patchreg                ; get pointer past patch data
  149.              move.w    #sizeof(PatchType), d0        ; get size of patch data
  150.              ext.l        d0
  151.              sub.l        d0, patchreg                    ; offset to start of patch data
  152.              move.l    patch(argsize), argszreg    ; get size of arguments
  153.              move.l    patch(retsize), retszreg    ; get size of return value
  154.             move.l    patch(trap), rsvoffset(a6)    ; set return address
  155.             rts
  156.             
  157.             ; call patch routine
  158.     call:
  159.             move.l    globreg, -(sp)                    ; setup global base register
  160.             move.l    patch(globalreg), globreg
  161.             sub.l        retszreg, sp                    ; reserve space for return value
  162.             move.l    patchreg, -(sp)                ; push patch data
  163.             sub.l        argszreg, sp                    ; reserve space for args
  164.             move.l    argszreg, d0                    ; copy args
  165.             lea        argoffset(a6), a0    
  166.             move.l    sp, a1
  167.             BlockMove
  168.             move.l    patch(addr), a0                ; call patch
  169.             jsr        (a0)
  170.             move.l    sp, a0                            ; copy return value
  171.             lea        argoffset(a6), a1
  172.             add.l        argszreg, a1
  173.             move.l    retszreg, d0
  174.             BlockMove
  175.              add.l        retszreg, sp                    ; pop return value
  176.              move.l    (sp)+, globreg                    ; restore global base register
  177.             rts
  178.     end:
  179.  
  180.             ; call to get bounds of function
  181.     extern _patch_code_bounds:
  182.             lea        @start, a0
  183.             lea        @end, a1
  184.             rts
  185.     }
  186. }
  187.  
  188. /* insert patch into list of patches, return head of list */
  189. static PatchType *PatchInsert(PatchType *list, PatchType *patch)
  190. {
  191.     patch->next = list;
  192.     return(patch);
  193. }
  194.  
  195. /* remove patch from list of patches, return head of list */
  196. static PatchType *PatchDelete(PatchType *list, PatchType *patch)
  197. {
  198.     PatchType *p = NULL;
  199.     PatchType *prev = NULL;
  200.     
  201.     for (p = list; p && p != patch; p = p->next)
  202.         prev = p;
  203.     if (p) {
  204.         if (prev)
  205.             prev->next = p->next;
  206.         else
  207.             list = p->next;
  208.     }
  209.     return(list);
  210. }
  211.  
  212. /* Patch to ExitToShell to remove our patches before exiting. It would
  213.     be possible to call a function PatchRemoveAll before exiting,
  214.     but in the case of an abnormal termination (e.g., some fatal error)
  215.     we must still remove the patches. */
  216. static pascal void PatchExitToShell(PatchType *patch)
  217. {
  218.     PatchType *p = NULL;
  219.     
  220.     /* We only remove the patch, but don't dispose of the memory it
  221.         occupies, since that would dispose of the code that called
  222.         this routine. Since the application is terminating the entire
  223.         heap is going to be disposed of anyway. */
  224.     for (p = gPatches; p; p = p->next)
  225.         PatchRemove(p);
  226. }
  227.  
  228. /* install the patch */
  229. void PatchInstall(PatchType *patch)
  230. {
  231.     if (! patch->installed) {
  232.         NSetTrapAddress((long) ((char *) patch + sizeof(PatchType)), patch->num, patch->type);
  233.         patch->installed = true;
  234.     }
  235. }
  236.  
  237. /* remove the patch */
  238. void PatchRemove(PatchType *patch)
  239. {
  240.     if (patch->installed) {
  241.         NSetTrapAddress((long) patch->trap, patch->num, patch->type);
  242.         patch->installed = false;
  243.     }
  244. }
  245.  
  246. /* create a patch */
  247. PatchType *PatchBegin(void *addr, short num, long argsize, long retsize, void *data)
  248. {
  249.     char *p, *q;
  250.     void *trap = NULL;
  251.     long globalreg = 0;
  252.     PatchType *patch = NULL;
  253.     static Boolean initialized;
  254.     
  255.     require(argsize >= 0 && (argsize & 1) == 0);
  256.     require(retsize >= 0 && (retsize & 1) == 0);
  257.     if (! initialized) {
  258.         initialized = true;
  259.         (void) PatchBegin(PatchExitToShell, _ExitToShell, 0, 0, NULL);
  260.     }
  261.     _patch_code_bounds();
  262.     asm {
  263.         move.l a0, p 
  264.         move.l a1, q
  265.     }
  266.     patch = (PatchType *) NewPtrClear(sizeof(PatchType) + q - p);
  267.     BlockMove(p, (char *) patch + sizeof(PatchType), q - p);
  268.     asm { move.l globreg, globalreg }
  269.     patch->num = num;
  270.     patch->data = data;
  271.     patch->argsize = argsize;
  272.     patch->retsize = retsize;
  273.     patch->globalreg = globalreg;
  274.     patch->type = TrapTypeGet(patch->num);
  275.     patch->addr = (void *) StripAddress(addr);
  276.     patch->trap = (void *) NGetTrapAddress(patch->num, patch->type);
  277.     gPatches = PatchInsert(gPatches, patch);
  278.     PatchInstall(patch);
  279.     return(patch);
  280. }
  281.  
  282. /* remove and dispose of a patch */
  283. void PatchEnd(PatchType *patch)
  284. {
  285.     if (patch) {
  286.         gPatches = PatchDelete(gPatches, patch);
  287.         PatchRemove(patch);
  288.         DisposPtr((Ptr) patch);
  289.     }
  290. }
  291.